package com.javaspeak.concurrency.lock.countdownlatch;

import java.util.Date;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;

/**
 * This example shows a usage of CountDownLatch.
 * <p>
 * CountDownLatch can be useful in situations where a parent process wishes
 * to spawn some child threads and wishes to wait until the child processes
 * have completed before resuming its execution.
 * <p>
 * This example uses an ExecutorService to start a number of Runnable 
 * implementations (Job) using the submit(..) method on each 
 * individual Runnable.  The submit(..) method does not block until the 
 * Runnable has finished. In order to allow the JobRunner to wait until all
 * the Runnables it has submitted have completed we can use a CountDownLatch.
 * <p>
 * The JobRunner creates a CountDownLatch and initializes it with a count down 
 * equal to the number of threads it wishes to spawn. Everytime it creates a 
 * job, the JobRunner passes the CountDownLatch through the constructor of the
 * Job. Once the JobRunner has submitted all of the jobs (Runnables) it calls
 * countDownLatch.await() to wait for the jobs to complete.
 * 
 * The implementation of the Job has a run() method which on completing
 * its processing, calls dountDownLatch.countDown(). When each of the jobs has
 * called dountDownLatch.countDown(), the count down will have been reduced to
 * zero and the JobRunner will stop blocking on countDownLatch.await() and 
 * resume its processing.
 *  
 * Note we could have called invokeAll(..) on a collectio of Runnable which 
 * would have blocked until all the Runnables had completed but for the purpose 
 * of this example we individually submitted each Runnable (Job) using the 
 * submit(..) method and used a CountDownLatch for determining when all 
 * Runnables had completed their processing.
 * 
 * @author John Dickerson
 */
public class JobRunner {
    
    Logger logger = Logger.getLogger( JobRunner.class );
    
    private ExecutorService executorService;
    private int numberJobs;
    
    public JobRunner( int numberJobs ){
    
        this.numberJobs = numberJobs;
        executorService = Executors.newFixedThreadPool( numberJobs );
    }
    
    
    public void runJobs() throws InterruptedException {
        
        CountDownLatch countDownLatch = new CountDownLatch( numberJobs );
        Random random = new Random( ( new Date() ).getTime() );
        
        logger.debug( "Starting jobs, please wait" );
        
        long startTime = System.currentTimeMillis();
        
        for ( int i=0; i<numberJobs; i++ ){
        
            executorService.submit( new Job( countDownLatch, random ) );
        }
        
        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        
        logger.debug( "Jobs completed in " + 
                ( endTime - startTime ) + " milliseconds" );
        
        executorService.shutdown();
    }
    
    
    public static void main( String args[] ) throws InterruptedException {
        
        BasicConfigurator.resetConfiguration();
        BasicConfigurator.configure();
        
        JobRunner jobRunner = new JobRunner( 10 );
        jobRunner.runJobs();
    }
}
